package main

import (
	"context"
	"encoding/json"
	"github.com/go-chi/chi/v5"
	"io"
	"net"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"strings"
	"time"

	"github.com/sirupsen/logrus"
	"github.com/spf13/viper"
)

// Config --- 数据结构定义 ---
type Config struct {
	Server ServerConfig `mapstructure:"server"`
	Users  []UserConfig `mapstructure:"users"`
}
type ServerConfig struct {
	Port       string `mapstructure:"port"`
	PublicRead bool   `mapstructure:"public_read"`
}
type UserConfig struct {
	Username   string   `mapstructure:"username"`
	Token      string   `mapstructure:"token"`
	AllowedIPs []string `mapstructure:"allowed_ips"`
}
type APIRequest struct {
	BucketName string `json:"bucket_name"`
	FolderPath string `json:"folder_path,omitempty"`
	ObjectName string `json:"object_name,omitempty"`
}
type APIResponse struct {
	Success bool        `json:"success"`
	Message string      `json:"message"`
	Data    interface{} `json:"data,omitempty"`
}

// AppConfig --- 全局变量 ---
var AppConfig Config
var Log *logrus.Logger

const dataDir = "./data"

// Init --- 初始化函数 ---
func Init() {
	Log = logrus.New()
	Log.SetFormatter(&logrus.JSONFormatter{
		TimestampFormat: "2006-01-02 15:04:05",
	})
	logFile, err := os.OpenFile("app.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
	if err == nil {
		multiWriter := io.MultiWriter(os.Stdout, logFile)
		Log.SetOutput(multiWriter)
	} else {
		Log.Info("无法打开 app.log 文件，日志将仅输出到控制台")
	}
	Log.SetLevel(logrus.InfoLevel)
	viper.SetConfigName("config")
	viper.SetConfigType("yaml")
	viper.AddConfigPath(".")
	if err := viper.ReadInConfig(); err != nil {
		Log.Fatalf("致命错误：无法读取配置文件: %s", err)
	}
	if err := viper.Unmarshal(&AppConfig); err != nil {
		Log.Fatalf("致命错误：无法解析配置到结构体: %v", err)
	}
	Log.Info("配置加载成功")
}

// RespondJSON --- 辅助函数 ---
func RespondJSON(w http.ResponseWriter, status int, payload interface{}) {
	response, err := json.Marshal(payload)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte(err.Error()))
		Log.Errorf("无法序列化响应: %v", err)
		return
	}
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(status)
	w.Write(response)
}
func serveFile(w http.ResponseWriter, r *http.Request) {
	fullPath := filepath.Join(dataDir, chi.URLParam(r, "username"), chi.URLParam(r, "bucket"), chi.URLParam(r, "*"))
	cleanedPath, _ := filepath.Abs(fullPath)
	cleanedDataDir, _ := filepath.Abs(dataDir)
	if !strings.HasPrefix(cleanedPath, cleanedDataDir) {
		http.Error(w, "Invalid path", http.StatusBadRequest)
		return
	}
	if _, err := os.Stat(fullPath); os.IsNotExist(err) {
		http.Error(w, "Object not found", http.StatusNotFound)
		return
	}
	http.ServeFile(w, r, fullPath)
}

// --- 中间件 ---

// AuthMiddleware CORRECTED: AuthMiddleware - REMOVED IP CHECK LOGIC
func AuthMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// 检查并提取 Token
		authHeader := r.Header.Get("Authorization")
		if !strings.HasPrefix(authHeader, "Bearer ") {
			RespondJSON(w, http.StatusUnauthorized, APIResponse{Success: false, Message: "Authorization header must be a Bearer token"})
			return
		}
		token := strings.TrimPrefix(authHeader, "Bearer ")

		// 验证 Token 是否存在
		var authorizedUser *UserConfig
		for _, user := range AppConfig.Users {
			if user.Token == token {
				u := user
				authorizedUser = &u
				break
			}
		}
		if authorizedUser == nil {
			RespondJSON(w, http.StatusUnauthorized, APIResponse{Success: false, Message: "Invalid token"})
			return
		}

		// 将用户信息放入上下文并放行
		ctx := context.WithValue(r.Context(), "username", authorizedUser.Username)
		next.ServeHTTP(w, r.WithContext(ctx))
	})
}

func RequestLoggerMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()
		next.ServeHTTP(w, r)
		Log.WithFields(logrus.Fields{
			"method":  r.Method,
			"path":    r.URL.Path,
			"ip":      r.RemoteAddr,
			"latency": time.Since(start),
		}).Info("Request processed")
	})
}

// --- 6. API 处理器 ---
func handleListBuckets(w http.ResponseWriter, r *http.Request) {
	username := r.Context().Value("username").(string)
	userPath := filepath.Join(dataDir, username)
	entries, err := os.ReadDir(userPath)
	if err != nil {
		if os.IsNotExist(err) {
			RespondJSON(w, http.StatusOK, APIResponse{Success: true, Message: "User directory does not exist, no buckets found", Data: []string{}})
			return
		}
		Log.Errorf("无法读取用户 '%s' 的目录: %v", username, err)
		RespondJSON(w, http.StatusInternalServerError, APIResponse{Success: false, Message: "Failed to read user directory"})
		return
	}
	var buckets []string
	for _, entry := range entries {
		if entry.IsDir() {
			buckets = append(buckets, entry.Name())
		}
	}
	RespondJSON(w, http.StatusOK, APIResponse{Success: true, Message: "Buckets retrieved successfully", Data: buckets})
}
func handleCreateBucket(w http.ResponseWriter, r *http.Request) {
	username := r.Context().Value("username").(string)
	var req APIRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		RespondJSON(w, http.StatusBadRequest, APIResponse{Success: false, Message: "Invalid JSON request body"})
		return
	}
	if req.BucketName == "" || strings.ContainsAny(req.BucketName, "/\\") {
		RespondJSON(w, http.StatusBadRequest, APIResponse{Success: false, Message: "Invalid bucket name"})
		return
	}
	bucketPath := filepath.Join(dataDir, username, req.BucketName)
	if err := os.MkdirAll(bucketPath, 0755); err != nil {
		Log.Errorf("用户 '%s' 创建桶 '%s' 失败: %v", username, req.BucketName, err)
		RespondJSON(w, http.StatusInternalServerError, APIResponse{Success: false, Message: "Failed to create bucket"})
		return
	}
	RespondJSON(w, http.StatusCreated, APIResponse{Success: true, Message: "Bucket created successfully"})
}
func handleListFolders(w http.ResponseWriter, r *http.Request) {
	username := r.Context().Value("username").(string)
	var req APIRequest
	json.NewDecoder(r.Body).Decode(&req)
	targetPath := filepath.Join(dataDir, username, req.BucketName, req.FolderPath)
	entries, err := os.ReadDir(targetPath)
	if err != nil {
		if os.IsNotExist(err) {
			RespondJSON(w, http.StatusNotFound, APIResponse{Success: false, Message: "Directory or bucket not found"})
			return
		}
		Log.Errorf("无法列出目录 '%s': %v", targetPath, err)
		RespondJSON(w, http.StatusInternalServerError, APIResponse{Success: false, Message: "Failed to list directory contents"})
		return
	}
	var items []map[string]interface{}
	for _, entry := range entries {
		itemType := "file"
		if entry.IsDir() {
			itemType = "folder"
		}
		items = append(items, map[string]interface{}{"name": entry.Name(), "type": itemType})
	}
	RespondJSON(w, http.StatusOK, APIResponse{Success: true, Message: "Contents listed successfully", Data: items})
}
func handleCreateFolder(w http.ResponseWriter, r *http.Request) {
	username := r.Context().Value("username").(string)
	var req APIRequest
	json.NewDecoder(r.Body).Decode(&req)
	if req.BucketName == "" || req.FolderPath == "" {
		RespondJSON(w, http.StatusBadRequest, APIResponse{Success: false, Message: "Bucket name and folder path are required"})
		return
	}
	folderPath := filepath.Join(dataDir, username, req.BucketName, req.FolderPath)
	if err := os.MkdirAll(folderPath, 0755); err != nil {
		Log.Errorf("用户 '%s' 创建文件夹 '%s' 失败: %v", username, folderPath, err)
		RespondJSON(w, http.StatusInternalServerError, APIResponse{Success: false, Message: "Failed to create folder"})
		return
	}
	RespondJSON(w, http.StatusCreated, APIResponse{Success: true, Message: "Folder created successfully"})
}
func handleDeleteFolder(w http.ResponseWriter, r *http.Request) {
	username := r.Context().Value("username").(string)
	var req APIRequest
	json.NewDecoder(r.Body).Decode(&req)
	if req.BucketName == "" || req.FolderPath == "" {
		RespondJSON(w, http.StatusBadRequest, APIResponse{Success: false, Message: "Bucket name and folder path are required"})
		return
	}
	folderPath := filepath.Join(dataDir, username, req.BucketName, req.FolderPath)
	if err := os.RemoveAll(folderPath); err != nil {
		Log.Errorf("用户 '%s' 删除文件夹 '%s' 失败: %v", username, folderPath, err)
		RespondJSON(w, http.StatusInternalServerError, APIResponse{Success: false, Message: "Failed to delete folder"})
		return
	}
	RespondJSON(w, http.StatusOK, APIResponse{Success: true, Message: "Folder and all its contents deleted successfully"})
}
func handleUploadFile(w http.ResponseWriter, r *http.Request) {
	username := r.Context().Value("username").(string)
	if err := r.ParseMultipartForm(10 << 20); err != nil {
		RespondJSON(w, http.StatusBadRequest, APIResponse{Success: false, Message: "Could not parse multipart form"})
		return
	}
	file, handler, err := r.FormFile("file")
	if err != nil {
		RespondJSON(w, http.StatusBadRequest, APIResponse{Success: false, Message: "Invalid file: 'file' key not found in form-data"})
		return
	}
	defer file.Close()
	bucketName := r.FormValue("bucket_name")
	folderPath := r.FormValue("folder_path")
	if bucketName == "" {
		RespondJSON(w, http.StatusBadRequest, APIResponse{Success: false, Message: "'bucket_name' is a required form field"})
		return
	}
	physicalPath := filepath.Join(dataDir, username, bucketName, folderPath, handler.Filename)
	dirPath := filepath.Dir(physicalPath)
	if err := os.MkdirAll(dirPath, 0755); err != nil {
		Log.Errorf("上传时创建目录失败 '%s': %v", dirPath, err)
		RespondJSON(w, http.StatusInternalServerError, APIResponse{Success: false, Message: "Could not create destination directory"})
		return
	}
	dst, err := os.Create(physicalPath)
	if err != nil {
		Log.Errorf("无法创建目标文件 '%s': %v", physicalPath, err)
		RespondJSON(w, http.StatusInternalServerError, APIResponse{Success: false, Message: "Could not save file"})
		return
	}
	defer dst.Close()
	if _, err := io.Copy(dst, file); err != nil {
		Log.Errorf("写入文件 '%s' 时出错: %v", physicalPath, err)
		RespondJSON(w, http.StatusInternalServerError, APIResponse{Success: false, Message: "Failed to write file to disk"})
		return
	}
	logicalPath := path.Join(username, bucketName, folderPath, handler.Filename)
	RespondJSON(w, http.StatusCreated, APIResponse{
		Success: true,
		Message: "File uploaded successfully. Note: existing file with the same name will be overwritten.",
		Data:    map[string]string{"path": logicalPath},
	})
}
func handleGetObjectURL(w http.ResponseWriter, r *http.Request) {
	username := r.Context().Value("username").(string)
	var req APIRequest
	json.NewDecoder(r.Body).Decode(&req)
	if req.BucketName == "" || req.ObjectName == "" {
		RespondJSON(w, http.StatusBadRequest, APIResponse{Success: false, Message: "Bucket name and object name are required"})
		return
	}
	objectRelativePath := filepath.Join(req.FolderPath, req.ObjectName)
	urlPath := filepath.Join("/", username, req.BucketName, objectRelativePath)
	finalURL := filepath.ToSlash(urlPath)
	RespondJSON(w, http.StatusOK, APIResponse{Success: true, Message: "URL retrieved successfully", Data: map[string]string{"url": finalURL}})
}
func handleDeleteFile(w http.ResponseWriter, r *http.Request) {
	username := r.Context().Value("username").(string)
	var req APIRequest
	json.NewDecoder(r.Body).Decode(&req)
	if req.BucketName == "" || req.ObjectName == "" {
		RespondJSON(w, http.StatusBadRequest, APIResponse{Success: false, Message: "Bucket name and object name are required"})
		return
	}
	objectRelativePath := filepath.Join(req.FolderPath, req.ObjectName)
	filePath := filepath.Join(dataDir, username, req.BucketName, objectRelativePath)
	if err := os.Remove(filePath); err != nil {
		if os.IsNotExist(err) {
			RespondJSON(w, http.StatusNotFound, APIResponse{Success: false, Message: "File not found"})
			return
		}
		Log.Errorf("用户 '%s' 删除文件 '%s' 失败: %v", username, filePath, err)
		RespondJSON(w, http.StatusInternalServerError, APIResponse{Success: false, Message: "Failed to delete file"})
		return
	}
	RespondJSON(w, http.StatusOK, APIResponse{Success: true, Message: "File deleted successfully"})
}

// --- 程序入口 ---
func main() {
	Init()

	r := chi.NewRouter()
	r.Use(RequestLoggerMiddleware)

	r.Route("/api", func(r chi.Router) {
		r.Use(AuthMiddleware)
		r.Post("/buckets/list", handleListBuckets)
		r.Post("/buckets/create", handleCreateBucket)
		r.Post("/folders/list", handleListFolders)
		r.Post("/folders/create", handleCreateFolder)
		r.Post("/folders/delete", handleDeleteFolder)
		r.Post("/objects/upload", handleUploadFile)
		r.Post("/objects/url", handleGetObjectURL)
		r.Post("/objects/delete", handleDeleteFile)
	})

	// 公共文件访问路由
	r.Get("/{username}/{bucket}/*", func(w http.ResponseWriter, r *http.Request) {
		// 如果是公开读，直接提供文件
		if AppConfig.Server.PublicRead {
			serveFile(w, r)
			return
		}

		// 如果是私有读，检查IP白名单
		clientIP, _, err := net.SplitHostPort(r.RemoteAddr)
		if err != nil {
			Log.Errorf("无法解析 GET 请求的远程地址 '%s': %v", r.RemoteAddr, err)
			http.Error(w, "Internal Server Error", http.StatusInternalServerError)
			return
		}

		isAllowed := false
		// 遍历所有用户的 IP 白名单
		for _, user := range AppConfig.Users {
			for _, allowedIP := range user.AllowedIPs {
				if clientIP == allowedIP {
					isAllowed = true
					break
				}
			}
			if isAllowed {
				break
			}
		}

		if isAllowed {
			// IP在白名单中，提供文件
			Log.WithFields(logrus.Fields{"ip": clientIP, "path": r.URL.Path}).Info("白名单 IP 访问私有文件")
			serveFile(w, r)
		} else {
			// IP不在白名单中，拒绝访问
			Log.WithFields(logrus.Fields{"ip": clientIP, "path": r.URL.Path}).Warn("GET 请求被拒绝：公开读关闭且 IP 不在任何白名单中")
			http.Error(w, "Access forbidden", http.StatusForbidden)
		}
	})

	Log.Infof("Starting server on port %s", AppConfig.Server.Port)
	if err := http.ListenAndServe(AppConfig.Server.Port, r); err != nil {
		Log.Fatalf("Failed to start server: %v", err)
	}
}
